
/**
 * 数据包装
 * @param data 数据源
 * @param ctor 包装构造函数
 * @returns 包装格式
 */
function processData<T extends URLSearchParams | FormData>(data:Record<string, any>, ctor: new () => T): T {
	data = dataFormatter(data)
	const instance = new ctor()
	for (const key in data) {
		instance.append(key, data[key])
	}
	return instance
}

/**
 * 数据解构
 * @param data 数据源
 * @returns 一维数据
 */
function dataFormatter(data) :Record<string, any> {
	const isPrimate = value => typeof value !== 'object' || value instanceof Blob
	const processData: Record<string, any> = {}
	const doFormatter = (data, parentKey = '') => {
		if (isPrimate(data)) return processData[parentKey] = data
		for (const key in data) {
			const k = parentKey === '' ? key : `[${key}]`
			const value = data[key]
			if (isPrimate(value)) {
				processData[parentKey + k] = value
			} else if (Array.isArray(value)) {
				value.forEach((item, index) => {
					const combinedKey = parentKey + k + `[${index}]`
					isPrimate(item) ? (processData[combinedKey] = item) : doFormatter(item, combinedKey)
				})
			} else if (value.constructor === Object) {
				doFormatter(value, parentKey + k)
			} else {
				doFormatter(JSON.parse(JSON.stringify(value)), parentKey + k)
			}
		}
		return processData
	}
	return doFormatter(data)
}

/**
 * 请求基础工具方法
 * @param { string } url 请求地址
 * @param { IRequestOptions } options 请求配置
 * @returns { Promise<T> }
 */
function request<T>(url:string, options: IRequestOptions): Promise<T> {
	return new Promise<T>((resolve, reject) => {
		fetch(url, options).then(res => {
			switch (options.responseType) {
				case "blob":
					return res.blob()
				case "arraybuffer":
					return res.arrayBuffer()
				case "text":
					return res.text()
				default:
					return res.json()
			}
		}).then(res => {
			resolve(res)
		}).catch(e => {
			reject(e)
		})
	})
}

/**
 * get请求工具方法
 * @param { string } url 请求地址
 * @param { Record<string,any> } data 请求参数
 * @param { IRequestOptions } options 请求配置
 * @returns { Promise<T> }
 */
export function get<T>(url:string, data:Record<string,any> = {}, options: IRequestOptions={}): Promise<T> {
	const path = new URL(url)
	let search = path.search
	let params = new URLSearchParams(data).toString()
	if (search.trim() !== "") {
		if (params.trim() !== "") params = '&' + params
	} else {
		if (params.trim() !== "") search = '?'
	}
	url = path.origin + path.pathname + search + params
	return request<T>(url, { ...options, method: 'GET' })
}

/**
 * post请求工具方法
 * @param { string } url 请求地址
 * @param { Record<string,any> } data 请求参数
 * @param { IRequestOptions } options 请求配置
 * @returns { Promise<T> }
 */
export function post<T>(url:string, data:Record<string,any>={}, options: IRequestOptions={}): Promise<T> {
	if(!options.headers) options.headers = { 'Content-Type': 'application/json' }
	const contentType = options.headers['Content-Type']
	let innerData: XMLHttpRequestBodyInit
	if (contentType.startsWith("multipart/form-data")) {
		innerData = processData(data, FormData)
		if(options.headers['Content-Type']) Reflect.deleteProperty(options.headers, 'Content-Type')
	} else if (contentType.startsWith("application/x-www-form-urlencoded")) {
		innerData = processData(data, URLSearchParams)
	} else{
		innerData = JSON.stringify(data)
	}
	options.body = innerData
	return request<T>(url, { ...options, method: 'POST' })
}

export default request
